#include <map>

#include <hude/base/log.hpp>
#include <hude/utils/parses.hpp>
#include <hude/safe/crc.hpp>

#include "plugs/plug.hpp"
#include "virt-gpio.hpp"

namespace ha
{
    namespace brick
    {
        namespace pl =  std::placeholders;

        /*********************************************************************
         * @brief 虚拟gpio模块
         */
        class gpio_t :
            public brick_t
        {
        public:
            //声明解析栈类型
            using parses_t  = hude::utils::parses_t<>;
            // GPIO列表
            using gpios_t   = std::map< uint16_t, _gpio_t >;
            // 命令处理
            using command_t = std::function< void( const pkg_gpio_t&, pkg_gpio_t& ) >;
            // 命令标识
            using cmd_t     = _gpio_t::cmd_t;
            // 命令处理队列
            using cmds_t    = std::map< cmd_t, command_t >;

        public:
            gpio_t( aio_t aio, const json_t& args ) :
                aio_( aio ), parsing_( false )
            {
                if( !init() || ! this->settings( args ) )
                {
                    llog_err( "<plug-gpio>", "ERROR: pulg init: gpio" );
                }
            }

            ~gpio_t()
            {
                deinit();
            }

            int send( const void* p, size_t n ) override { _recv( p, n ); return n; }

            void close() override { deinit(); }

            bool settings( const json_t& args ) override;

            void commands();

        private:
            bool init();
            void deinit();
            void parse();

            void _recv( const void*, size_t );

            bool on_frame( parses_t::citr_t&, parses_t::citr_t&, bool& );

        private:
            aio_t            aio_;
            parses_t         parses_;
            gpios_t          gpios_;
            cmds_t           cmds_;
            std::atomic_flag parsing_;
        };

        /*********************************************************************
         * @brief 设置参数
         */
        inline bool gpio_t::settings( const json_t& args )
        {
            // llog_dug( "<plug-gpio>", "\t\t config rfid: %s", args.dump().c_str() );

            return true;
        }

        /*********************************************************************
         * @brief 初始化
         */
        bool gpio_t::init()
        {
            commands();

            parses_.observer_pre( std::bind( &gpio_t::on_frame, this, pl::_1, pl::_2,  pl::_3 ) );

            return true;
        }

        /*********************************************************************
         * @brief 反初始化
         */
        void gpio_t::deinit()
        {
        }

        /*********************************************************************
         * @brief 接收数据
         */
        void gpio_t::_recv( const void* p, size_t n )
        {
            // llog_dug( "<plug-gpio>", "\t\t <<< Recv buffer[ %s ]", hude::utils::to_hex( (const uint8_t*)p, n ).c_str() );

            parses_.buffer_push( p, n );

            // 在异步任务池执行缓冲区数据解析
            if ( n ) aio_->task_commit( [ = ] { this->parse(); } );
        }

        /*********************************************************************
         * @brief 解析缓冲中存在的所有数据
         */
        void gpio_t::parse()
        {
            // 无效数据丢弃事件
            auto drop = []( parses_t::citr_t b, parses_t::citr_t e ) -> void
            {
                llog_war( "<plug-gpio>", "\t\t droped bytes: %u(%s)", e - b, hude::utils::to_hex( &(*b), e - b ).c_str() );
            };

            if( ! parsing_.test_and_set() )
            {
                parses_.parse_all( &pkg_gpio_t::scaner, drop );

                parsing_.clear();
            }
        }

        /*********************************************************************
         * @brief 桢订阅处理函数
         *      参数b和e为数据桢处于缓冲区的头尾区间
         *      返回值为是否停止路由给解析栈的下一个订阅函数, true为停止
         */
        bool gpio_t::on_frame( parses_t::citr_t& b, parses_t::citr_t& e, bool& )
        {
            if( b == e ) return true;

            hude::buffer_t buffer_req{ b, e };
            hude::buffer_t buffer_res{ b, e };

            pkg_gpio_t *req = (pkg_gpio_t*)&(buffer_req[0]);
            pkg_gpio_t *res = (pkg_gpio_t*)&(buffer_res[0]);

            auto itr = cmds_.find( (cmd_t)req->gpio.command );

            if( itr == cmds_.end() )
            {
                llog_err( "<plug-gpio>", "ERROR: Not support cmd: %d", req->gpio.command );

                return false;
            }

            itr->second( *req, *res );

            if( this->cb_recv_ )
            {
                res->head.crc = res->get_crc();

                // llog_dug( "<plug-gpio>", "\t\t >>> Send buffer[ %s ]", hude::utils::to_hex( buffer_res ).c_str() );

                this->cb_recv_( buffer_res );
            }

            return true;
        }

        /*********************************************************************
         * @brief 设置命令处理
         */
        inline void gpio_t::commands()
        {
            //-----------------------------------
            // OPENED
            cmds_.emplace( cmd_t::emCMD_OPENED, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                res.gpio.flags = ( this->gpios_.find( req.gpio.ident.id ) == this->gpios_.end() )? 0U : 1U;
                res.gpio.result = 0U;
            } );

            //-----------------------------------
            // OPEN
            cmds_.emplace( cmd_t::emCMD_OPEN, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                auto itr = this->gpios_.emplace( req.gpio.ident.id, req.gpio );
                res.gpio.result = itr.second? 0U : 1U;
            } );

            //-----------------------------------
            // CLOSE
            cmds_.emplace( cmd_t::emCMD_CLOSE, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                auto itr = this->gpios_.find( req.gpio.ident.id );
                res.gpio.result = ( itr == this->gpios_.end() )? 1U : 0U;
                if( res.gpio.result == 0 ) this->gpios_.erase( itr );
            } );

            //-----------------------------------
            // GET_DIR
            cmds_.emplace( cmd_t::emCMD_GET_DIR, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                auto itr = this->gpios_.find( req.gpio.ident.id );

                if( itr == this->gpios_.end() )
                {
                    res.gpio.result = 1U;
                    return;
                } else {
                    res.gpio.result = 0U;
                }

                res.gpio.direction = itr->second.direction;
            } );

            //-----------------------------------
            // SET_DIR
            cmds_.emplace( cmd_t::emCMD_SET_DIR, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                auto itr = this->gpios_.find( req.gpio.ident.id );

                if( itr == this->gpios_.end() )
                {
                    res.gpio.result = 1U;
                    return;
                } else {
                    res.gpio.result = 0U;
                }

                itr->second.direction = req.gpio.direction;
            } );

            //-----------------------------------
            // GET
            cmds_.emplace( cmd_t::emCMD_GET, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                auto itr = this->gpios_.find( req.gpio.ident.id );

                if( itr == this->gpios_.end() )
                {
                    res.gpio.result = 1U;
                    return;
                } else {
                    res.gpio.result = 0U;
                }

                res.gpio.value = itr->second.value;
            } );

            //-----------------------------------
            // SET
            cmds_.emplace( cmd_t::emCMD_SET, [=]( const pkg_gpio_t& req, pkg_gpio_t& res ) {
                auto itr = this->gpios_.find( req.gpio.ident.id );

                if( itr == this->gpios_.end() )
                {
                    res.gpio.result = 1U;
                    return;
                } else {
                    res.gpio.result = 0U;
                }

                itr->second.value = req.gpio.value;
            } );
        }

    } //namespace brick
} //namespace ha

/*********************************************************************
 * @brief 工厂函数 
 */
EXTERN_C ha::pbrick_t _factory_brick( ha::aio_t aio, const ha::json_t& cfg )
{
    return ha::pbrick_t( new ha::brick::gpio_t{ aio, cfg } );
}